/**
  ******************************************************************************
  * @file    usbd_cdc_core.c
  * @author  MCD Application Team
  * @version V1.2.0
  * @date    09-November-2015
  * @brief   This file provides the high layer firmware functions to manage the
  *          following functionalities of the USB CDC Class:
  *           - Initialization and Configuration of high and low layer
  *           - Enumeration as CDC Device (and enumeration for each implemented memory interface)
  *           - OUT/IN data transfer
  *           - Command IN transfer (class requests management)
  *           - Error management
  *
  *  @verbatim
  *
  *          ===================================================================
  *                                CDC Class Driver Description
  *          ===================================================================
  *           This driver manages the "Universal Serial Bus Class Definitions for Communications Devices
  *           Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus
  *           Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007"
  *           This driver implements the following aspects of the specification:
  *             - Device descriptor management
  *             - Configuration descriptor management
  *             - Enumeration as CDC device with 2 data endpoints (IN and OUT) and 1 command endpoint (IN)
  *             - Requests management (as described in section 6.2 in specification)
  *             - Abstract Control Model compliant
  *             - Union Functional collection (using 1 IN endpoint for control)
  *             - Data interface class

  *           @note
  *             For the Abstract Control Model, this core allows only transmitting the requests to
  *             lower layer dispatcher (ie. usbd_cdc_vcp.c/.h) which should manage each request and
  *             perform relative actions.
  *
  *           These aspects may be enriched or modified for a specific user application.
  *
  *            This driver doesn't implement the following aspects of the specification
  *            (but it is possible to manage these features with some modifications on this driver):
  *             - Any class-specific aspect relative to communication classes should be managed by user application.
  *             - All communication classes other than PSTN are not managed
  *
  *  @endverbatim
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "usbd_cdc_core.h"
#include "usbd_desc.h"
#include "usbd_req.h"


/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @{
  */


/** @defgroup usbd_cdc
  * @brief usbd core module
  * @{
  */

/** @defgroup usbd_cdc_Private_TypesDefinitions
  * @{
  */
/**
  * @}
  */


/** @defgroup usbd_cdc_Private_Defines
  * @{
  */

#define USB_CDC_IDLE         0
#define USB_CDC_BUSY         1
#define USB_CDC_ZLP          2

/**
  * @}
  */


/** @defgroup usbd_cdc_Private_Macros
  * @{
  */
/**
  * @}
  */


/** @defgroup usbd_cdc_Private_FunctionPrototypes
  * @{
  */

/*********************************************
   CDC Device library callbacks
 *********************************************/
uint8_t  usbd_cdc_Init(void*  pdev, uint8_t cfgidx);
uint8_t  usbd_cdc_DeInit(void*  pdev, uint8_t cfgidx);
uint8_t  usbd_cdc_Setup(void*  pdev, USB_SETUP_REQ* req);
uint8_t  usbd_cdc_EP0_RxReady(void* pdev);
uint8_t  usbd_cdc_DataIn(void* pdev, uint8_t epnum);
uint8_t  usbd_cdc_DataOut(void* pdev, uint8_t epnum);
uint8_t  usbd_cdc_SOF(void* pdev);

/*********************************************
   CDC specific management functions
 *********************************************/
static void Handle_USBAsynchXfer(void* pdev);
static uint8_t*  USBD_cdc_GetCfgDesc(uint8_t speed, uint16_t* length);
#ifdef USE_USB_OTG_HS
	static uint8_t*  USBD_cdc_GetOtherCfgDesc(uint8_t speed, uint16_t* length);
#endif
/**
  * @}
  */

/** @defgroup usbd_cdc_Private_Variables
  * @{
  */
extern CDC_IF_Prop_TypeDef  APP_FOPS;
extern uint8_t USBD_DeviceDesc   [USB_SIZ_DEVICE_DESC];

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t usbd_cdc_CfgDesc  [USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END ;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t usbd_cdc_OtherCfgDesc  [USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END ;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN static __IO uint32_t  usbd_cdc_AltSet  __ALIGN_END = 0;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t USB_Rx_Buffer   [CDC_DATA_MAX_PACKET_SIZE] __ALIGN_END ;

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t APP_Rx_Buffer   [APP_RX_DATA_SIZE] __ALIGN_END ;


#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END ;

uint32_t APP_Rx_ptr_in  = 0;
uint32_t APP_Rx_ptr_out = 0;
uint32_t APP_Rx_length  = 0;

uint8_t  USB_Tx_State = USB_CDC_IDLE;

static uint32_t cdcCmd = 0xFF;
static uint32_t cdcLen = 0;

/* CDC interface class callbacks structure */
USBD_Class_cb_TypeDef  USBD_CDC_cb = {
	usbd_cdc_Init,
	usbd_cdc_DeInit,
	usbd_cdc_Setup,
	NULL,                 /* EP0_TxSent, */
	usbd_cdc_EP0_RxReady,
	usbd_cdc_DataIn,
	usbd_cdc_DataOut,
	usbd_cdc_SOF,
	NULL,
	NULL,
	USBD_cdc_GetCfgDesc,
#ifdef USE_USB_OTG_HS
	USBD_cdc_GetOtherCfgDesc, /* use same cobfig as per FS */
#endif /* USE_USB_OTG_HS  */
};

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t usbd_cdc_CfgDesc[USB_CDC_CONFIG_DESC_SIZ]  __ALIGN_END = {
	/*Configuration Descriptor*/
	0x09,   /* bLength: Configuration Descriptor size */
	USB_CONFIGURATION_DESCRIPTOR_TYPE,      /* bDescriptorType: Configuration */
	USB_CDC_CONFIG_DESC_SIZ,                /* wTotalLength:no of returned bytes */
	0x00,
	0x02,   /* bNumInterfaces: 2 interface */
	0x01,   /* bConfigurationValue: Configuration value */
	0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
	0xC0,   /* bmAttributes: self powered */
	0x32,   /* MaxPower 0 mA */

	/*---------------------------------------------------------------------------*/

	/*Interface Descriptor */
	0x09,   /* bLength: Interface Descriptor size */
	USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: Interface */
	/* Interface descriptor type */
	0x00,   /* bInterfaceNumber: Number of Interface */
	0x00,   /* bAlternateSetting: Alternate setting */
	0x01,   /* bNumEndpoints: One endpoints used */
	0x02,   /* bInterfaceClass: Communication Interface Class */
	0x02,   /* bInterfaceSubClass: Abstract Control Model */
	0x01,   /* bInterfaceProtocol: Common AT commands */
	0x00,   /* iInterface: */

	/*Header Functional Descriptor*/
	0x05,   /* bLength: Endpoint Descriptor size */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x00,   /* bDescriptorSubtype: Header Func Desc */
	0x10,   /* bcdCDC: spec release number */
	0x01,

	/*Call Management Functional Descriptor*/
	0x05,   /* bFunctionLength */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x01,   /* bDescriptorSubtype: Call Management Func Desc */
	0x00,   /* bmCapabilities: D0+D1 */
	0x01,   /* bDataInterface: 1 */

	/*ACM Functional Descriptor*/
	0x04,   /* bFunctionLength */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
	0x02,   /* bmCapabilities */

	/*Union Functional Descriptor*/
	0x05,   /* bFunctionLength */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x06,   /* bDescriptorSubtype: Union func desc */
	0x00,   /* bMasterInterface: Communication class interface */
	0x01,   /* bSlaveInterface0: Data Class Interface */

	/*Endpoint 2 Descriptor*/
	0x07,                           /* bLength: Endpoint Descriptor size */
	USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
	CDC_CMD_EP,                     /* bEndpointAddress */
	0x03,                           /* bmAttributes: Interrupt */
	LOBYTE(CDC_CMD_PACKET_SZE),     /* wMaxPacketSize: */
	HIBYTE(CDC_CMD_PACKET_SZE),
#ifdef USE_USB_OTG_HS
	0x10,                           /* bInterval: */
#else
	0xFF,                           /* bInterval: */
#endif /* USE_USB_OTG_HS */

	/*---------------------------------------------------------------------------*/

	/*Data class interface descriptor*/
	0x09,   /* bLength: Endpoint Descriptor size */
	USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: */
	0x01,   /* bInterfaceNumber: Number of Interface */
	0x00,   /* bAlternateSetting: Alternate setting */
	0x02,   /* bNumEndpoints: Two endpoints used */
	0x0A,   /* bInterfaceClass: CDC */
	0x00,   /* bInterfaceSubClass: */
	0x00,   /* bInterfaceProtocol: */
	0x00,   /* iInterface: */

	/*Endpoint OUT Descriptor*/
	0x07,   /* bLength: Endpoint Descriptor size */
	USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType: Endpoint */
	CDC_OUT_EP,                        /* bEndpointAddress */
	0x02,                              /* bmAttributes: Bulk */
	LOBYTE(CDC_DATA_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
	HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
	0x00,                              /* bInterval: ignore for Bulk transfer */

	/*Endpoint IN Descriptor*/
	0x07,   /* bLength: Endpoint Descriptor size */
	USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType: Endpoint */
	CDC_IN_EP,                         /* bEndpointAddress */
	0x02,                              /* bmAttributes: Bulk */
	LOBYTE(CDC_DATA_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
	HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
	0x00                               /* bInterval: ignore for Bulk transfer */
} ;

#ifdef USE_USB_OTG_HS
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
	#if defined ( __ICCARM__ ) /*!< IAR Compiler */
		#pragma data_alignment=4
	#endif
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
__ALIGN_BEGIN uint8_t usbd_cdc_OtherCfgDesc[USB_CDC_CONFIG_DESC_SIZ]  __ALIGN_END = {
	0x09,   /* bLength: Configuration Descriptor size */
	USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION,
	USB_CDC_CONFIG_DESC_SIZ,
	0x00,
	0x02,   /* bNumInterfaces: 2 interfaces */
	0x01,   /* bConfigurationValue: */
	0x04,   /* iConfiguration: */
	0xC0,   /* bmAttributes: */
	0x32,   /* MaxPower 100 mA */

	/*Interface Descriptor */
	0x09,   /* bLength: Interface Descriptor size */
	USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: Interface */
	/* Interface descriptor type */
	0x00,   /* bInterfaceNumber: Number of Interface */
	0x00,   /* bAlternateSetting: Alternate setting */
	0x01,   /* bNumEndpoints: One endpoints used */
	0x02,   /* bInterfaceClass: Communication Interface Class */
	0x02,   /* bInterfaceSubClass: Abstract Control Model */
	0x01,   /* bInterfaceProtocol: Common AT commands */
	0x00,   /* iInterface: */

	/*Header Functional Descriptor*/
	0x05,   /* bLength: Endpoint Descriptor size */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x00,   /* bDescriptorSubtype: Header Func Desc */
	0x10,   /* bcdCDC: spec release number */
	0x01,

	/*Call Management Functional Descriptor*/
	0x05,   /* bFunctionLength */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x01,   /* bDescriptorSubtype: Call Management Func Desc */
	0x00,   /* bmCapabilities: D0+D1 */
	0x01,   /* bDataInterface: 1 */

	/*ACM Functional Descriptor*/
	0x04,   /* bFunctionLength */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
	0x02,   /* bmCapabilities */

	/*Union Functional Descriptor*/
	0x05,   /* bFunctionLength */
	0x24,   /* bDescriptorType: CS_INTERFACE */
	0x06,   /* bDescriptorSubtype: Union func desc */
	0x00,   /* bMasterInterface: Communication class interface */
	0x01,   /* bSlaveInterface0: Data Class Interface */

	/*Endpoint 2 Descriptor*/
	0x07,                           /* bLength: Endpoint Descriptor size */
	USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
	CDC_CMD_EP,                     /* bEndpointAddress */
	0x03,                           /* bmAttributes: Interrupt */
	LOBYTE(CDC_CMD_PACKET_SZE),     /* wMaxPacketSize: */
	HIBYTE(CDC_CMD_PACKET_SZE),
	0xFF,                           /* bInterval: */

	/*---------------------------------------------------------------------------*/

	/*Data class interface descriptor*/
	0x09,   /* bLength: Endpoint Descriptor size */
	USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: */
	0x01,   /* bInterfaceNumber: Number of Interface */
	0x00,   /* bAlternateSetting: Alternate setting */
	0x02,   /* bNumEndpoints: Two endpoints used */
	0x0A,   /* bInterfaceClass: CDC */
	0x00,   /* bInterfaceSubClass: */
	0x00,   /* bInterfaceProtocol: */
	0x00,   /* iInterface: */

	/*Endpoint OUT Descriptor*/
	0x07,   /* bLength: Endpoint Descriptor size */
	USB_ENDPOINT_DESCRIPTOR_TYPE,      /* bDescriptorType: Endpoint */
	CDC_OUT_EP,                        /* bEndpointAddress */
	0x02,                              /* bmAttributes: Bulk */
	0x40,                              /* wMaxPacketSize: */
	0x00,
	0x00,                              /* bInterval: ignore for Bulk transfer */

	/*Endpoint IN Descriptor*/
	0x07,   /* bLength: Endpoint Descriptor size */
	USB_ENDPOINT_DESCRIPTOR_TYPE,     /* bDescriptorType: Endpoint */
	CDC_IN_EP,                        /* bEndpointAddress */
	0x02,                             /* bmAttributes: Bulk */
	0x40,                             /* wMaxPacketSize: */
	0x00,
	0x00                              /* bInterval */
};
#endif /* USE_USB_OTG_HS  */

/**
  * @}
  */

/** @defgroup usbd_cdc_Private_Functions
  * @{
  */

/**
  * @brief  usbd_cdc_Init
  *         Initialize the CDC interface
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
uint8_t  usbd_cdc_Init(void*  pdev,
                       uint8_t cfgidx)
{
	uint8_t* pbuf;

	/* Open EP IN */
	DCD_EP_Open(pdev,
	            CDC_IN_EP,
	            CDC_DATA_IN_PACKET_SIZE,
	            USB_OTG_EP_BULK);

	/* Open EP OUT */
	DCD_EP_Open(pdev,
	            CDC_OUT_EP,
	            CDC_DATA_OUT_PACKET_SIZE,
	            USB_OTG_EP_BULK);

	/* Open Command IN EP */
	DCD_EP_Open(pdev,
	            CDC_CMD_EP,
	            CDC_CMD_PACKET_SZE,
	            USB_OTG_EP_INT);

	pbuf = (uint8_t*)USBD_DeviceDesc;
	pbuf[4] = DEVICE_CLASS_CDC;
	pbuf[5] = DEVICE_SUBCLASS_CDC;

	/* Initialize the Interface physical components */
	APP_FOPS.pIf_Init();

	/* Prepare Out endpoint to receive next packet */
	DCD_EP_PrepareRx(pdev,
	                 CDC_OUT_EP,
	                 (uint8_t*)(USB_Rx_Buffer),
	                 CDC_DATA_OUT_PACKET_SIZE);

	return USBD_OK;
}

/**
  * @brief  usbd_cdc_Init
  *         DeInitialize the CDC layer
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
uint8_t  usbd_cdc_DeInit(void*  pdev,
                         uint8_t cfgidx)
{
	/* Open EP IN */
	DCD_EP_Close(pdev,
	             CDC_IN_EP);

	/* Open EP OUT */
	DCD_EP_Close(pdev,
	             CDC_OUT_EP);

	/* Open Command IN EP */
	DCD_EP_Close(pdev,
	             CDC_CMD_EP);

	/* Restore default state of the Interface physical components */
	APP_FOPS.pIf_DeInit();

	return USBD_OK;
}

/**
  * @brief  usbd_cdc_Setup
  *         Handle the CDC specific requests
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
uint8_t  usbd_cdc_Setup(void*  pdev,
                        USB_SETUP_REQ* req)
{
	switch(req->bmRequest & USB_REQ_TYPE_MASK) {
		/* CDC Class Requests -------------------------------*/
		case USB_REQ_TYPE_CLASS :

			/* Check if the request is a data setup packet */
			if(req->wLength) {
				/* Check if the request is Device-to-Host */
				if(req->bmRequest & 0x80) {
					/* Get the data to be sent to Host from interface layer */
					APP_FOPS.pIf_Ctrl(req->bRequest, CmdBuff, req->wLength);

					/* Send the data to the host */
					USBD_CtlSendData(pdev,
					                 CmdBuff,
					                 req->wLength);
				} else { /* Host-to-Device request */
					/* Set the value of the current command to be processed */
					cdcCmd = req->bRequest;
					cdcLen = req->wLength;

					/* Prepare the reception of the buffer over EP0
					Next step: the received data will be managed in usbd_cdc_EP0_TxSent()
					function. */
					USBD_CtlPrepareRx(pdev,
					                  CmdBuff,
					                  req->wLength);
				}
			} else { /* No Data request */
				/* Transfer the command to the interface layer */
				APP_FOPS.pIf_Ctrl(req->bRequest, NULL, 0);
			}

			return USBD_OK;

		default:
			USBD_CtlError(pdev, req);
			return USBD_FAIL;

		/* Standard Requests -------------------------------*/
		case USB_REQ_TYPE_STANDARD:
			switch(req->bRequest) {
				case USB_REQ_GET_DESCRIPTOR:
					USBD_CtlError(pdev, req);
					return USBD_FAIL;

				case USB_REQ_GET_INTERFACE :
					USBD_CtlSendData(pdev,
					                 (uint8_t*)&usbd_cdc_AltSet,
					                 1);
					break;

				case USB_REQ_SET_INTERFACE :
					if((uint8_t)(req->wValue) < USBD_ITF_MAX_NUM) {
						usbd_cdc_AltSet = (uint8_t)(req->wValue);
					} else {
						/* Call the error management function (command will be nacked */
						USBD_CtlError(pdev, req);
					}

					break;
			}
	}

	return USBD_OK;
}

/**
  * @brief  usbd_cdc_EP0_RxReady
  *         Data received on control endpoint
  * @param  pdev: device instance
  * @retval status
  */
uint8_t  usbd_cdc_EP0_RxReady(void*  pdev)
{
	if(cdcCmd != NO_CMD) {
		/* Process the data */
		APP_FOPS.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen);

		/* Reset the command variable to default value */
		cdcCmd = NO_CMD;
	}

	return USBD_OK;
}


/**
  * @brief  usbd_audio_DataIn
  *         Data sent on non-control IN endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t  usbd_cdc_DataIn(void* pdev, uint8_t epnum)
{
	uint16_t USB_Tx_ptr;
	uint16_t USB_Tx_length;

	if(USB_Tx_State == USB_CDC_BUSY) {
		if(APP_Rx_length == 0) {
			USB_Tx_State = USB_CDC_IDLE;
		} else {
			if(APP_Rx_length > CDC_DATA_IN_PACKET_SIZE) {
				USB_Tx_ptr = APP_Rx_ptr_out;
				USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;

				APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
				APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
			} else {
				USB_Tx_ptr = APP_Rx_ptr_out;
				USB_Tx_length = APP_Rx_length;

				APP_Rx_ptr_out += APP_Rx_length;
				APP_Rx_length = 0;

				if(USB_Tx_length == CDC_DATA_IN_PACKET_SIZE) {
					USB_Tx_State = USB_CDC_ZLP;
				}
			}

			/* Prepare the available data buffer to be sent on IN endpoint */
			DCD_EP_Tx(pdev,
			          CDC_IN_EP,
			          (uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
			          USB_Tx_length);
			return USBD_OK;
		}
	}

	/* Avoid any asynchronous transfer during ZLP */
	if(USB_Tx_State == USB_CDC_ZLP) {
		/*Send ZLP to indicate the end of the current transfer */
		DCD_EP_Tx(pdev,
		          CDC_IN_EP,
		          NULL,
		          0);

		USB_Tx_State = USB_CDC_IDLE;
	}

	return USBD_OK;
}

/**
  * @brief  usbd_cdc_DataOut
  *         Data received on non-control Out endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t  usbd_cdc_DataOut(void* pdev, uint8_t epnum)
{
	uint16_t USB_Rx_Cnt;

	/* Get the received data buffer and update the counter */
	USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;

	/* USB data will be immediately processed, this allow next USB traffic being
	NAKed till the end of the application Xfer */
	APP_FOPS.pIf_DataRx(USB_Rx_Buffer, USB_Rx_Cnt);

	/* Prepare Out endpoint to receive next packet */
	DCD_EP_PrepareRx(pdev,
	                 CDC_OUT_EP,
	                 (uint8_t*)(USB_Rx_Buffer),
	                 CDC_DATA_OUT_PACKET_SIZE);

	return USBD_OK;
}

/**
  * @brief  usbd_audio_SOF
  *         Start Of Frame event management
  * @param  pdev: instance
  * @param  epnum: endpoint number
  * @retval status
  */
uint8_t  usbd_cdc_SOF(void* pdev)
{
	static uint32_t FrameCount = 0;

	if(FrameCount++ == CDC_IN_FRAME_INTERVAL) {
		/* Reset the frame counter */
		FrameCount = 0;

		/* Check the data to be sent through IN pipe */
		Handle_USBAsynchXfer(pdev);
	}

	return USBD_OK;
}

/**
  * @brief  Handle_USBAsynchXfer
  *         Send data to USB
  * @param  pdev: instance
  * @retval None
  */
static void Handle_USBAsynchXfer(void* pdev)
{
	uint16_t USB_Tx_ptr;
	uint16_t USB_Tx_length;

	if(USB_Tx_State == USB_CDC_IDLE) {
		if(APP_Rx_ptr_out == APP_RX_DATA_SIZE) {
			APP_Rx_ptr_out = 0;
		}

		if(APP_Rx_ptr_out == APP_Rx_ptr_in) {
			USB_Tx_State = USB_CDC_IDLE;
			return;
		}

		if(APP_Rx_ptr_out > APP_Rx_ptr_in) { /* rollback */
			APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out;

		} else {
			APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out;

		}

#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
		APP_Rx_length &= ~0x03;
#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */

		if(APP_Rx_length > CDC_DATA_IN_PACKET_SIZE) {
			USB_Tx_ptr = APP_Rx_ptr_out;
			USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;

			APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
			APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
			USB_Tx_State = USB_CDC_BUSY;
		} else {
			USB_Tx_ptr = APP_Rx_ptr_out;
			USB_Tx_length = APP_Rx_length;

			APP_Rx_ptr_out += APP_Rx_length;
			APP_Rx_length = 0;

			if(USB_Tx_length == CDC_DATA_IN_PACKET_SIZE) {
				USB_Tx_State = USB_CDC_ZLP;
			} else {
				USB_Tx_State = USB_CDC_BUSY;
			}
		}

		DCD_EP_Tx(pdev,
		          CDC_IN_EP,
		          (uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
		          USB_Tx_length);
	}
}

/**
  * @brief  USBD_cdc_GetCfgDesc
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
static uint8_t*  USBD_cdc_GetCfgDesc(uint8_t speed, uint16_t* length)
{
	*length = sizeof(usbd_cdc_CfgDesc);
	return usbd_cdc_CfgDesc;
}

/**
  * @brief  USBD_cdc_GetCfgDesc
  *         Return configuration descriptor
  * @param  speed : current device speed
  * @param  length : pointer data length
  * @retval pointer to descriptor buffer
  */
#ifdef USE_USB_OTG_HS
static uint8_t*  USBD_cdc_GetOtherCfgDesc(uint8_t speed, uint16_t* length)
{
	*length = sizeof(usbd_cdc_OtherCfgDesc);
	return usbd_cdc_OtherCfgDesc;
}
#endif
/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
